iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
1
Modern Web

React.js 從 【0】 到【1】推坑計畫 系列 第 15

【Day15 路由神器 - React-router 】

  • 分享至 

  • xImage
  •  
講到 SPA(single page application)與傳統網頁的差異,應該很多人會想到傳統網頁跳頁往往會有一段載入時間,導致畫面空白,讓使用者需要對著電腦螢幕發呆吧。

而 SPA 的出現大大改善了 web 應用的使用者體驗,切換頁面可以在一瞬間達成,今天就要來介紹在 react 中切換路由的神器 - React router。

React-router

React router 使我們可以選擇在切換路由時要切換的元件,例如一個應用可能每一頁都會有 Navbar ,過往的作法可能是每頁中都會放置同一個 Navbar 的 template ,切換頁面時儘管 Navbar 沒有改變,還是會重新渲染。然而透過 React router 我們可以控制需要切換的元件(component),減少重複渲染,提升應用效能。

首先要先安裝相關套件

npm i react-router
npm i react-router-dom

還記得前幾天以來我們做了一個 counter 元件跟一個 form 元件嗎?
今天就簡單實現可以在兩個組件中做切換
會有兩個 link ,分別切換到對應的路由
表單填答送出後會自動切換回 counter page
(可能不是很有意義的應用程式,但的確對了解 router 非常有幫助)
開始囉!

在 App.js 中引入以下套件

import  { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';

接著改變 App.js 的程式碼

const App = () => {
  const [count, setCount] = useState(0);
  const [formDone, setFormDone] = useState(false)
  return (
    <Router>
      <div className="App">
        <p>react router example</p>
        <ul>
          <li><Link to="counter">Counter</Link></li>
          <li><Link to="form">Form</Link></li>
        </ul>
        <Switch>
          <Route path='/counter' component={Counter}/>
          <Route path='/form' component={Form}/>
          />
        </Switch>
      </div>
    </Router>
  );
}
  • 要使用 router 功能的 component 需要被 Router tag 包住
  • Link 可以想像成 HTML 的 <a> tag ,用來導引到不同路由,to 對應的是 Route 中定義的路由
  • Switch 內放置各種路由狀況,而不需要隨著路由改變的元件,如上面提到的 Navbar,通常不會包在 Switch 裡。
  • Route 定義各個路由與該路由要渲染的 component

比較重要的就是 Route 了,在這可以定義 URL路由(path),還可以定義該路由要顯示的 component。
比較細心的讀者應該會發現一個問題:
之前的範例中, counter 跟 form 都有傳 props 下去欸,但是這邊看起來沒有辦法傳 props 欸!

如果要傳 props 給 router 中的 component,需要改寫成以下程式

<Route path='/counter' component={() => <Counter count={count} setCount={setCount}/>}/>
<Route path='/form' component={() => <Form formDone={formDone} setFormDone={setFormDone}/>}/>

https://ithelp.ithome.com.tw/upload/images/20190919/20113277gFBnIxjHW6.png

這時 router 就設置完成了,網頁雖然很陽春,卻有了 router 的功能。
counter 跟 form 的 link 因為放在 swich 外,因此不管切到哪個路由都會存在,點擊它們會在下面顯示相對應的 component。

最後來實作一個功能,有時候我們不想要都需要點擊 link 才能跳頁,像這個範例也許我們希望在送出表單後自動跳到 counter 頁面。
我們可以透過 history 達到這個功能

Form.js中

import React,{ useState } from 'react'
import { withRouter } from "react-router";

const Form = (props) => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        props.setFormDone(true);
        setEmail('');
        setPassword('');
        props.history.push('/counter');
    }
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <input name="email" type="text" onChange={(e) => setEmail(e.target.value)} value={email} />
                <input name="password" type="password" onChange={(e) => setPassword(e.target.value)} value={password} />
                <input type="submit" value="submit" />
            </form>
            {props.formDone ? "成功填寫表單" : "表單未完成"}
        </div>
    )
}

export default withRouter(Form);

要使用這個功能,首先從 react-router 引入 withRouter,它是一個 higher-order-component(HOC),不了解的讀者建議可以去了解一下,簡單來說就是使用它包住原先的 component 後,可以賦予 component 一些它提供的功能。

而 withRouter 提供的是 history 這個 props
在 handleSubmit 的最後呼叫了 props.history.push('/counter')
這會自動把你的路由“推動”到 counter 的 router,因此就達到送出表單後自動跳頁的功能了。
history 還有提供其他種函式可以使用,就交給讀者自行研究囉~

有了 router 之後,在搭配前幾天學習的概念,其實已經可以做出非常多應用了,從明天開始會進入比較進階的概念 - state management,明天見。


上一篇
【Day 14】Form in React
下一篇
【Day 16】state management - redux 基礎概念
系列文
React.js 從 【0】 到【1】推坑計畫 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
Austin7L
iT邦新手 5 級 ‧ 2022-08-15 11:08:35

您好,我再App.js import react-router-dom後,出現下列錯誤
https://ithelp.ithome.com.tw/upload/images/20220815/20119847bpPSowpi28.jpg

Austin7L iT邦新手 5 級 ‧ 2022-08-15 11:20:41 檢舉

這個解決了,版本問題,將Switch > Routes

0
hdjoan
iT邦新手 4 級 ‧ 2024-12-09 15:43:10

withRouter目前也被取代了,可參考以下:

import { useState } from "react";
import { useNavigate } from 'react-router-dom';

const Form = (prop) => {
    const navigate = useNavigate();

    //...

    const handleSubmit = (e) => {
        e.preventDefault(); //阻止事件的預設行為
        navigate('/counter');
    }

    return (
        //...
    )
}

export default Form;

我要留言

立即登入留言